<?php
/**
* 內链处理
* @author moufer<moufer@163.com>
* @copyright www.modoer.com
*/
!defined('IN_MUDDER') && exit('Access Denied');

class ms_internal_link
{
	/**
	 * 需要进行占位符替换的标签(不包含a标签)
	 * 例如：<input type="txtbox" />
	 * @var array
	 */
	static $html_tags = array('!DOCTYPE','abbr','acronym','address','applet','area','article','aside','audio','b','base','basefont',
		'bdi','bdo','big','blockquote','body','br','button','canvas','caption','center','cite','col','colgroup','datalist',
		'dd','del','details','dir','div','dfn','dialog','dl','dt','em','fieldset','figcaption',
		'figure','font','footer','form','h[1-6]{1}','header','hr','html','i','img','input','ins','keygen',
		'label','legend','li','link','map','mark','meta','meter','noframes','noscript','ol',
		'output','p','param','pre','progress','q','rp','rt','ruby','s','samp','section','small','source','span',
		'strike','strong','sub','summary','sup','table','tbody','td','tfoot','th','thead','time',
		'tr','track','tt','u','ul','var','video','wbr');

	/**
	 * 需要整条完整的标签进行占位符替换，即标签之间所有文字都不参与內链关键字替换
	 * (a标签排在最前)
	 * 例如： <option value="xx">bbbb</option>
	 * @var array
	 */
	static $html_repalce_tags = array('a','head','select','optgroup','option','embed','script','object','nav','frameset',
		'frame','iframe','style','textarea','menu','menuitem','code','kbd','command','title');

	/**
	 * 准备替换的内容
	 * @var string
	 */
	protected $content = '';
	/**
	 * 內链关键字和链接
	 * @var array
	 */
	protected $links = array();
	/**
	 * 占位符替换的内容暂存数组
	 * @var array
	 */
	protected $placeholder = array();
	/**
	 * 一些参数设置
	 * @var null
	 */
	protected $options = null;

	/**
	 * 实例化內链处理累
	 * @param string $content 准备替换的内容
	 * @param array $links   內链链接
	 * @param array $options 参数
	 */
	public function __construct($content, $links, $options = null)
	{
		$this->set_content($content);
		foreach ($links as $link) {
			$this->add_link($link['title'], $link['url'], $link['note'], $link['target']);
		}
		if(!$options) $this->set_option($options);
	}

	/**
	 * 增加一个新的內链
	 * @param string $title 链接标题
	 * @param string $url   链接地址
	 */
	public function add_link($title, $url, $note = '', $target = '')
	{
		$key = md5($title);
		$this->links[$key] = array(
			'title'  => $title,
			'url' 	 => $url,
			'note' 	 => $note,
			'target' => $target,
		);
	}

	/**
	 * 设置准备替换的文本内容
	 * @param string $content
	 */
	public function set_content($content)
	{
		$this->content= $content;
	}

	/**
	 * 设置一个或一组配置参数（未使用）
	 * @param string $variable 参数名称
	 * @param mixed $value    参数值
	 */
	public function set_option($variable, $value = null)
	{
		if(!$this->options) $this->options = new ms_attr;

		if(!is_array($value) || is_null($value)) return;
		if(is_array($variable)) {
			foreach ($variable as $key => $val) {
				$this->options->set_attr($key, $val);
			}
		} else {
			$this->options->set_attr($variable, $value);
		}
		return true;
	}

	/**
	 * 执行內链处理
	 * @return string 已经加入內链的文本内容
	 */
	public function process()
	{
		//print_r($this->content);
		//echo "\n\n\n\n";
		$content = $this->replace_tag(); //替换标签为占位符，防止标签内属性被內链关键字替换
		//print_r($content);
		//echo "\n\n\n\n";
		$content = $this->repalce_links($content); //关键字替换成內链
		//print_r($content);
		//echo "\n\n\n\n";
		$content = $this->restore_tag($content); //占位符恢复成标签
		//返回 最终内容
		return $content;
	}

	/**
	 * 替换html标签为占位符，完成內链处理后，再从占位符恢复标签
	 * @return [type] [description]
	 */
	protected function replace_tag()
	{
		$placeholder = array();
		$content = $this->content;

		//删除注解
		$content = preg_replace("/<!--.*?-->/s", '', $content);

		//a标记排在最前
		foreach (static::$html_repalce_tags as $pattern) {
			$content = preg_replace_callback (
				"/<({$pattern}).*?>.*?<\/\s*{$pattern}\s*>/is", 
				function($matches) use (&$placeholder) {
					$i = array_push($placeholder, $matches[0]) - 1; //array_push返回的是数组数量，-1表示获取数组的下标值
					return '{placeholder tag='.$matches[1].' i='.$i.'}';
				},
				$content
			);
		}

		//html标签替换为占位符
		foreach (static::$html_tags as $pattern) {
			if(!$pattern) continue;
			$content = preg_replace_callback (
				"/(<({$pattern})\s+.*?>)/i", 
				function($matches) use (&$placeholder) {
					$i = array_push($placeholder, $matches[1]) - 1; //array_push返回的是数组数量，-1表示获取数组的下标值
					return '{placeholder tag='.$matches[2].' i='.$i.'}';
				},
				$content
			);
		}

		$this->placeholder = $placeholder; //占位符，恢复时进行替换
		//print_r($this->placeholder);
		return $content;
	}

	/**
	 * 占位符恢复为标签
	 * @param  string $content 准备恢复的文本内容
	 * @return string
	 */
	protected function restore_tag($content)
	{
		//还原占位符的内容
		$replace = function($content, &$matches, &$placeholder) {
			$tag = $matches[1];
			$keyid = $matches[2];
			return str_replace('{placeholder tag='.$tag.' i='.$keyid.'}', $placeholder[$keyid], $content);
		};

		//先还原完全替换的标签
		$tag_p = implode('|', static::$html_repalce_tags);
		if(preg_match("/\{placeholder tag=($tag_p) i=([0-9]+)\}/", $content, $matches)) {
			$content = $replace($content, $matches, $this->placeholder);
		}

		//再还原其他html标签
		$i = 0;
		while (preg_match("/\{placeholder tag=(.*?) i=([0-9]+)\}/", $content, $matches)) {
			$content = $replace($content, $matches, $this->placeholder);
			if(++ $i > 1000) break;
		}

		return $content;
	}

	/**
	 * 处理內链，替换关键字为內链链接
	 * @param  string $content 准备替换的文本内容
	 * @return string
	 */
	protected function repalce_links($content)
	{
		if(!$this->links) return $content;
		$search = $replace = array();
		foreach ($this->links as $key => $link) {
			$search[] = $link['title'];
			//避免链接数组中有包含相同标题关键字的链接出现链接多次替换，先进行占位，随后再替换
			$replace[] = "{link key=$key}";
		}
		//替换关键字为占位符
		$content = str_ireplace($search, $replace, $content);
		//把占位符替换成链接
		$search = $replace = array();
		foreach ($this->links as $key => $link) {
			$search[] = "{link key=$key}";
			$title = $link['note']?" title=\"{$link['note']}\"":'';
			$target = $link['target']?" target=\"{$link['target']}\"":'';
			$replace[] = '<a data-name="internal-link" href="'.$link['url'].'"'.$title.$target.'>'.$link['title'].'</a>';
		}
		$content = str_ireplace($search, $replace, $content);

		return $content;
	}

}

/** end */